/************************************************************************
* meshgen_chunk.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
************************************************************************/

#include "common.h"
#include "graphics.h"
#include "thread.h"
#include "list.h"

#include "meshgen.h"
#include "map.h"

static struct {
	thread_t *thread;
	mutex_t *mutex;
	uint32_t state;
	mapobj_t *objs;
} meshgen_data = {
	NULL,
	NULL,
	VLSTATE_EXIT,
	NULL
};

static void meshgen_chunk(mapobj_t *o)
{
	chunk_t *ch;
	v3_t p;
	pos_t bp;
	int b = 0;

	ch = o->chunk;

	for (bp.x=0; bp.x<16; bp.x++) {
		for (bp.y=0; bp.y<16; bp.y++) {
			for (bp.z=0; bp.z<16; bp.z++) {
				p.x = bp.x;
				p.y = bp.y;
				p.z = bp.z;
				b += meshgen_block(o,&ch->blocks[bp.x][bp.y][bp.z],&p,&bp);
			}
		}
	}

	if (!b)
		return;

	if (ch->mesh.obj) {
		int i;
		int k;
		mesh_t *m;

		for (i=0; i<4; i++) {
			for (k=0; k<o->objs[i].meshes->length; k++) {
				m = ((mesh_t**)o->objs[i].meshes->data)[k];
				if (m->vao.state == 1)
					m->vao.state = 2;
			}
		}
	}

	ch->mesh.obj = o;

	render_map_chunk(o);
}

static void *meshgen_thread(void *data)
{
	mapobj_t *o;
	while (meshgen_data.state == VLSTATE_PLAY) {
		if (!meshgen_data.objs) {
			delay(10);
			continue;
		}

		mutex_lock(meshgen_data.mutex);
		o = list_pop(&meshgen_data.objs);
		mutex_unlock(meshgen_data.mutex);
		if (!o)
			continue;

		meshgen_chunk(o);
	}

	mutex_lock(meshgen_data.mutex);
	while (meshgen_data.objs) {
		o = list_pop(&meshgen_data.objs);
		if (!o)
			continue;
	}
	mutex_unlock(meshgen_data.mutex);

	return NULL;
}

/* initialise meshgen */
int meshgen_init()
{
	meshgen_data.state = VLSTATE_PLAY;
	meshgen_data.mutex = mutex_create();
	meshgen_data.thread = thread_create(meshgen_thread,NULL);

	return 0;
}

/* deinit meshgen, and wait for the thread to exit */
void meshgen_exit()
{
	meshgen_data.state = VLSTATE_EXIT;
	thread_wait(meshgen_data.thread);

	mutex_free(meshgen_data.mutex);
	meshgen_data.mutex = NULL;
	meshgen_data.thread = NULL;
}

/* add a chunk to the list for generating a mesh, called by UPDATE and LOAD map triggers */
void meshgen_add_chunk(chunk_t *ch, pos_t *p)
{
	mapobj_t *o;
	int i;

	if (meshgen_data.state != VLSTATE_PLAY)
		return;

	if ((ch->state&CHUNK_STATE_EMPTY) == CHUNK_STATE_EMPTY)
		return;

	mutex_lock(meshgen_data.mutex);
	o = meshgen_data.objs;
	while (o) {
		if (o->pos.x == p->x && o->pos.y == p->y && o->pos.z == p->z) {
			mutex_unlock(meshgen_data.mutex);
			return;
		}
		o = o->next;
	}
	mutex_unlock(meshgen_data.mutex);

	if (ch->mesh.obj) {
		int k;
		mesh_t *m;

		/* TODO: the below will (may) segfault, so don't */
		return;

		o = ch->mesh.obj;

		for (i=0; i<4; i++) {
			for (k=0; k<o->objs[i].meshes->length; k++) {
				m = ((mesh_t**)o->objs[i].meshes->data)[k];
				m->v->length = 0;
				m->n->length = 0;
				m->t->length = 0;
				m->i->length = 0;
			}
		}
	}else{
		o = malloc(sizeof(mapobj_t));
		if (!o) {
			vlprintf(CN_WARN,"allocation failure in meshgen_add_chunk (%d,%d,%d)",p->x,p->y,p->z);
			return;
		}

		for (i=0; i<4; i++) {
			o->objs[i].meshes = NULL;
		}

		o->chunk = ch;
		o->pos.x = p->x;
		o->pos.y = p->y;
		o->pos.z = p->z;

		array_init(&o->blockobjs,ARRAY_TYPE_PTR);

		o->bounds.min.x = o->pos.x;
		o->bounds.min.y = o->pos.y;
		o->bounds.min.z = o->pos.z;
		o->bounds.max.x = o->pos.x+16.0;
		o->bounds.max.y = o->pos.y+16.0;
		o->bounds.max.z = o->pos.z+16.0;
	}

	mutex_lock(meshgen_data.mutex);
	meshgen_data.objs = list_push(&meshgen_data.objs,o);
	mutex_unlock(meshgen_data.mutex);
}

/* free a mesh's chunk, called by UNLOAD map trigger */
void meshgen_free_chunk(chunk_t *ch, pos_t *p)
{
}
